﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using System.Xml.Serialization;
using System.Drawing;

namespace QbiconEditor
{
    public static class QHelper
    {
        private static QMapCollectorItem workMap;
        private static XmlSerializer xmlSerializer;

        public static QMapCollectorItem CurrentWorkQItem
        {
          get
          {
            return workMap;
          }
          set
          {
            workMap = value;
          }
        }

        public static QMap CurrentMap
        {
          get
          {
            if (workMap != null)
            {
              if (workMap.qmap != null)
              {
                return workMap.qmap;
              }
            }

            return null;
          }
          set
          {
            if (workMap != null)
            {
             workMap.qmap = value;
            }
          }
        }

        public static int ActiveLayerNum
        {
          get
          {
            if (workMap != null)
            {
              return workMap.activeLayer;
            }
            else
            {
              return -1;
            }
          }
          set
          {
            if (workMap != null)
            {
              if (workMap.qmap != null)
              {
                if (workMap.qmap.Layers.Count - 1 >= value)
                {
                  workMap.activeLayer = value;
                }
              }
            }
          }
        }

        public static QMapLayer CurrentLayer
        {
          get
          {
            if ((workMap != null) && ActiveLayerNum > -1)
            {
              if (workMap.qmap.Layers.Count - 1 >= ActiveLayerNum)
              {
                return workMap.qmap.Layers[ActiveLayerNum];
              }
              else
              {
                return null;
              }
            }
            else
            {
              return null;
            }
          }
        }

        public static QObject FindQObject(string name)
        {
            if (CurrentLayer == null)
            {
                return null;
            }

            return CurrentLayer.Objects.Find(
                            delegate(QObject qob)
                            {
                                return qob.Name == name;
                            });
        }

        public static string FindFreeObjectName(string name)
        {
            if (CurrentLayer == null)
            {
                return string.Empty;
            }

            string newName = name;

            if (FindQObject(name) != null)
            {

                for (int i = 0; i < Int32.MaxValue; i++)
                {
                    newName = string.Format("{0}{1}", name, i.ToString());
                    if (FindQObject(newName) == null)
                    {
                        return newName;
                    }
                }
            }

            return newName;
        }
        public static string FindFreeLayerName(string name)
        {
          if (CurrentMap != null)
          {
              string newName = name;
              int i = 0;

              while (CurrentMap.Layers.Find(layer => layer.Name == newName) != null)
              {
                i++;
                newName += i.ToString();
              }

              return newName;
          }

          return string.Empty;
        }

        public static string AddNewQObject(QObject qob)
        {
            if (CurrentLayer == null)
            {
                return string.Empty;
            }

            qob.Name = FindFreeObjectName(qob.Name);

            CurrentLayer.Objects.Add(qob);

            return qob.Name;
        }

        public static QObject AddNewQObject(string name)
        {
           return AddNewQObject(name,
                          new TextureInfo() { KeyName = QbiconConstants.DEFAULT_QOBJECT_TEXTURE},
                          Color.FromName(QbiconConstants.DEFAULT_QOBJECT_COLORNAME),
                          CreateNewPolygone(),
                          true);
        }

        public static QObject AddNewQObject(string name, TextureInfo texture, Color color, QPolygone poly, bool visible)
        {
            if (CurrentLayer == null)
            {
                return null;
            }

            string newName = FindFreeObjectName(name);

            QObject qObj = new QObject();
            qObj.Name = newName;

            if (poly != null)
            {
              qObj.Poly = CopyQPoly(poly);
            }
            else
            {
              qObj.Poly = new QPolygone();
            }

            qObj.Color = color;
            qObj.Texture = CopyTextureInfo(texture);
            qObj.Visible = visible;

            CurrentLayer.Objects.Insert(0, qObj);

            return qObj;
        }

        public static TextureInfo CopyTextureInfo(TextureInfo textureInfo)
        {
            TextureInfo tinfo = new TextureInfo();
            tinfo.DestinationRect = textureInfo.DestinationRect;
            tinfo.KeyName = textureInfo.KeyName;
            tinfo.SourceRect = textureInfo.SourceRect;
            textureInfo.WrapMode = textureInfo.WrapMode;

            return tinfo;
        }

        public static void RemoveQObject(QObject qob)
        {
            if (CurrentLayer == null)
            {
                return;
            }

            CurrentLayer.Objects.Remove(qob);
        }

        public static QObject LoadQObject(string fileName)
        {
            if (File.Exists(fileName) == true)
            {
                FileStream fs = new FileStream(fileName, FileMode.Open, FileAccess.Read);

                if (fs.CanRead)
                {
                    QObject qObj = new QObject();
                    xmlSerializer = new XmlSerializer(qObj.GetType());

                    qObj = (xmlSerializer.Deserialize(fs) as QObject);
                    fs.Close();
                    fs.Dispose();

                    return qObj;
                }
                else
                {
                    fs.Close();
                    fs.Dispose();
                }
            }

            return null;
        }
        
        public static void SaveQObject(string fileName, QObject qObj)
        {
            if (qObj == null)
            {
                return;
            }

            if (File.Exists(fileName) == true)
            {
                File.Delete(fileName);
            }

            FileStream fs = new FileStream(fileName, FileMode.OpenOrCreate, FileAccess.Write);
            xmlSerializer = new XmlSerializer(qObj.GetType());
            xmlSerializer.Serialize(fs, qObj);

            fs.Flush();
            fs.Close();
            fs.Dispose();
        }

        public static QObject QObjectAtPoint(float pX, float pY, bool visibleOnly, bool pointsOnScreen)
        {
            if (CurrentLayer != null)
            {
              QPoint qp = new QPoint() { X = pX, Y = pY };

              if (pointsOnScreen == true)
              {
                qp = QGraphicsHelper.GetPointOnMap(pX, pY);
              }

              foreach (QObject qob in CurrentLayer.Objects)
              {
                if (visibleOnly == true)
                {
                  if ((qob.Visible == true) && (QGraphicsHelper.IsPointInPolygone(qp, qob.Poly.Points.ToArray()) == true))
                  {
                    return qob;
                  }
              }
            else
            {
                if (QGraphicsHelper.IsPointInPolygone(qp, qob.Poly.Points.ToArray()) == true)
                {
                    return qob;
                }
            }
              }
            }

            return null;
        }
        public static QObject[] FindObjectsInRect(QRectangleF Rect)
        {
          if (CurrentLayer != null)
          {
            List<QObject> result = new List<QObject>();
            foreach (QObject qob in CurrentLayer.Objects)
            {
              if (QGraphicsHelper.IsPolyInRect(qob.Poly, Rect) == true)
              {
                result.Add(qob);
              }
            }

            return result.ToArray();
          }
          else
          {
            return null;
          }
        }

        public static QObject GetPointParentObject(QPoint qp)
        {
          if (CurrentLayer != null)
          {
            foreach (QObject qob in CurrentLayer.Objects)
            {
              if (qob.Poly.Points.Contains(qp) == true)
              {
                return qob;
              }
            }
          }

          return null;
        }

        public static QPoint[] FindPointsInRect(QRectangleF Rect)
        {
          QRectangleF r = QGraphicsHelper.FixRectangle(Rect);

          if (CurrentLayer != null)
          {
            List<QPoint> result = new List<QPoint>();

            foreach (QObject qob in CurrentLayer.Objects)
            {
              foreach (QPoint qp in qob.Poly.Points)
              {
                if (QGraphicsHelper.IsPointInRect(qp.X, qp.Y, r) == true)
                {
                  result.Add(qp);
                }
              }
            }

            return result.ToArray();
          }
          else
          {
            return null;
          }
        }

        public static void ChangeObjectPoints(QObject qob, float dx, float dy)
        {
          if (qob != null)
          {
            ChangeAllPoints(qob.Poly.Points.ToArray(), dx, dy);
          }
        }

        public static void ChangeAllPoints(QPoint[] points, float dx, float dy)
        {
          if (points != null)
          {
            foreach (QPoint qp in points)
            {
              qp.X += dx;
              qp.Y += dy;
            }
          }
        }

        public static QMap LoadQMap(string fileName)
        {
            if (File.Exists(fileName) == true)
            {
              FileStream fs = new FileStream(fileName, FileMode.Open, FileAccess.Read);

              try
              {
                
                if (fs.CanRead)
                {
                  QMap qmap = new QMap();
                  xmlSerializer = new XmlSerializer(qmap.GetType());
                  qmap = (xmlSerializer.Deserialize(fs) as QMap);
                  fs.Close();
                  fs.Dispose();
                  return qmap;
                }
              }
              catch
              {
                //TODO: ???
                //Process Exception
              }
              finally
              {
                fs.Close();
                fs.Dispose();
              }
            }

            return null;
        }

        public static QPreferences LoadPreferences(string fileName)
        {
          if (File.Exists(fileName) == true)
          {
            FileStream fs = new FileStream(fileName, FileMode.Open, FileAccess.Read);

            try
            {
              if (fs.CanRead)
              {
                QPreferences qprf = new QPreferences();
                xmlSerializer = new XmlSerializer(qprf.GetType());
                qprf = (xmlSerializer.Deserialize(fs) as QPreferences);

                fs.Close();
                fs.Dispose();

                return qprf;
              }
            }
            catch
            {
              //TODO: ???
              //Process Exception
            }
            finally
            {
              fs.Close();
              fs.Dispose();
            }
          }

          return null;
        }

        public static void SaveQMap(string fileName, QMap qmap)
        {
            if (qmap == null)
            {
                return;
            }

            if (File.Exists(fileName) == true)
            {
                File.Delete(fileName);
            }

            FileStream fs = new FileStream(fileName, FileMode.OpenOrCreate, FileAccess.Write);
            xmlSerializer = new XmlSerializer(qmap.GetType());
            xmlSerializer.Serialize(fs, qmap);

            fs.Flush();
            fs.Close();
            fs.Dispose();
        }

        public static void SavePreferences(string fileName, QPreferences prefs)
        {
            if (prefs == null)
            {
              return;
            }

            if (File.Exists(fileName) == true)
            {
              File.Delete(fileName);
            }

            FileStream fs = new FileStream(fileName, FileMode.OpenOrCreate, FileAccess.Write);
            xmlSerializer = new XmlSerializer(prefs.GetType());
            xmlSerializer.Serialize(fs, prefs);

            fs.Flush();
            fs.Close();
            fs.Dispose();
        }

        public static QPoint[] GetAllObjectsPoints(QObject[] objects)
        {
          List<QPoint> points = new List<QPoint>();

          foreach (QObject qob in objects)
          {
            points.AddRange(qob.Poly.Points);
          }

          return points.ToArray();
        }

        public static void SendObjectToBack(QObject qob)
        {
          CurrentLayer.Objects.Remove(qob);
          CurrentLayer.Objects.Add(qob);
        }
        public static void SendObjectToFront(QObject qob)
        {
          CurrentLayer.Objects.Remove(qob);
          CurrentLayer.Objects.Insert(0, qob);
        }

        public static QPolygone CreateNewPolygone()
        {
          QPolygone poly = new QPolygone();

          poly.Points.Add(new QPoint() { X = QbiconConstants.DEFAULT_QOBJECT_LEFT, Y = QbiconConstants.DEFAULT_QOBJECT_TOP });
          poly.Points.Add(new QPoint() { X = QbiconConstants.DEFAULT_QOBJECT_LEFT + QbiconConstants.DEFAULT_QOBJECT_WIDTH, Y = QbiconConstants.DEFAULT_QOBJECT_TOP });
          poly.Points.Add(new QPoint() { X = QbiconConstants.DEFAULT_QOBJECT_LEFT + QbiconConstants.DEFAULT_QOBJECT_WIDTH, Y = QbiconConstants.DEFAULT_QOBJECT_TOP + QbiconConstants.DEFAULT_QOBJECT_HEIGHT });
          poly.Points.Add(new QPoint() { X = QbiconConstants.DEFAULT_QOBJECT_LEFT, Y = QbiconConstants.DEFAULT_QOBJECT_TOP + QbiconConstants.DEFAULT_QOBJECT_HEIGHT });
          
          return poly;
        }
       
        public static QPoint CopyQPoint(QPoint qp)
        {
          return new QPoint() {X = qp.X, Y = qp.Y};
        }

        public static QPolygone CopyQPoly(QPolygone qpoly)
        {
          QPolygone p = new QPolygone();

          foreach(QPoint qp in qpoly.Points)
          {
            p.Points.Add(CopyQPoint(qp));
          }

          return p;
        }

        public static QObject CopyQObject(QObject qobject)
        {
          QObject q = new QObject();

          q.Color = qobject.Color;
          q.CustomProperties.AddRange(qobject.CustomProperties.ToArray());
          q.Name = qobject.Name;
          q.Poly = CopyQPoly(qobject.Poly);
          q.Texture = qobject.Texture;
          q.Type = qobject.Type;
          q.Visible = qobject.Visible;

          return q;
        }
        public static QMapLayer CopyQLayer(QMapLayer layer)
        {

          QMapLayer l = new QMapLayer();
          l.CustomProperties.AddRange(layer.CustomProperties.ToArray());
          l.Name = layer.Name;

          foreach (QObject qob in layer.Objects)
          {
            l.Objects.Add(CopyQObject(qob));
          }

          l.Type = layer.Type;
          l.Visible = layer.Visible;

          return l;
        }
        public static QMap CopyQMap(QMap map)
        {
          QMap m = new QMap();
          m.BackgroundColor = map.BackgroundColor;
          m.CustomProperties.AddRange(map.CustomProperties.ToArray());

          foreach (QMapLayer layer in map.Layers)
          {
            m.Layers.Add(CopyQLayer(layer));
          }

          m.Name = map.Name;
          m.Type = map.Type;

          return m;
        }
    }
}
